using UnityEditor;
using UnityEngine;
using System;
using System.Reflection;
using System.Collections.Generic;

public class UnitTestEditor : EditorWindow
{
    public class TestEntry
    {
        public Type scriptType;
        public string scriptName;
        public string methodName;
    }
    public static UnitTestEditor unitTestController;

    private Vector2 m_scrollPosition;
    private List<TestEntry> m_testEntryList;
    private List<TestEntry> m_uniqueEntryList;
    private List<bool> m_foldoutStateList;

    [MenuItem("Tools/UnitTestWindow &#t")]
    public static void UnitTestWindow()
    {
        List<TestEntry> testEntryList = GetSceneTestEntries();
        UnitTestEditor.unitTestController = EditorWindow.GetWindow(typeof(UnitTestEditor), true) as UnitTestEditor;
        UnitTestEditor.unitTestController.Initialize(testEntryList);
    }

    public void Initialize(List<TestEntry> testEntryList)
    {
        m_testEntryList = testEntryList;
        int foldoutCount = GetUniqueScriptCount(testEntryList);
        m_uniqueEntryList = GetUniqueScriptList(testEntryList);
        m_foldoutStateList = new List<bool>(foldoutCount);
        for (int i = 0; i < foldoutCount; ++i)
        {
            m_foldoutStateList.Add(true);
        }
    }

    void OnGUI()
    {
        if (GUILayout.Button("Refresh"))
        {
            List<TestEntry> testEntryList = GetSceneTestEntries();
            UnitTestEditor.unitTestController.Initialize(testEntryList);
        }

        m_scrollPosition = EditorGUILayout.BeginScrollView(m_scrollPosition);
        for (int i = 0; i < m_foldoutStateList.Count; ++i)
        {
            m_foldoutStateList[i] = EditorGUILayout.Foldout(m_foldoutStateList[i], m_uniqueEntryList[i].scriptName);
            if (m_foldoutStateList[i])
            {
                for (int j = 0; j < m_testEntryList.Count; ++j)
                {
                    if (m_testEntryList[j].scriptName == m_uniqueEntryList[i].scriptName)
                    {
                        if (GUILayout.Button(m_testEntryList[j].methodName))
                        {
                            Type scriptType = m_testEntryList[j].scriptType;
                            scriptType.InvokeMember(
                             m_testEntryList[j].methodName,
                             BindingFlags.DeclaredOnly |
                             BindingFlags.Public |
                             BindingFlags.NonPublic |
                             BindingFlags.Instance |
                             BindingFlags.InvokeMethod,
                             null,
                             Activator.CreateInstance(scriptType),
                             null);
                        }
                    }
                }
            }
        }
        EditorGUILayout.EndScrollView();
    }
    
    public static List<TestEntry> GetSceneTestEntries()
    {
        List<TestEntry> testEntryList = new List<TestEntry>();
        Assembly assembly = Assembly.GetExecutingAssembly();
        Type[] assemblyTypes = assembly.GetTypes();
        for (int i = 0; i < assemblyTypes.Length; ++i)
        {
            if (assemblyTypes[i].Namespace == "TestNamespace")
            {
                MethodInfo[] scriptMethodInfos = assemblyTypes[i].GetMethods(
                    BindingFlags.DeclaredOnly
                    | BindingFlags.Public
                    | BindingFlags.NonPublic
                    | BindingFlags.Instance);

                foreach (MethodInfo methodInfo in scriptMethodInfos)
                {
                    Attribute[] customAttributes = Attribute.GetCustomAttributes(methodInfo);
                    foreach (Attribute customAttribute in customAttributes)
                    {
                        if (customAttribute.GetType() == typeof(TestAttribute))
                        {
                            TestEntry testEntry = new TestEntry() { scriptType = assemblyTypes[i], scriptName = assemblyTypes[i].Name, methodName = methodInfo.Name };
                            testEntryList.Add(testEntry);
                        }
                    }
                }
            }
        }

        return testEntryList;
    }

    public static int GetUniqueScriptCount(List<TestEntry> testEntryList)
    {
        int uniqueScriptCount = 0;
        for (int i = 0; i < testEntryList.Count; ++i)
        {
            if (i == 0)
            {
                ++uniqueScriptCount;
            }
            for (int j = 0; j < i; ++j)
            {
                if (testEntryList[j].scriptName == testEntryList[i].scriptName)
                {
                    break;
                }
                if (j == i - 1)
                {
                    ++uniqueScriptCount;
                }
            }
        }

        return uniqueScriptCount;
    }

    public static List<TestEntry> GetUniqueScriptList(List<TestEntry> testEntryList)
    {
        List<TestEntry> uniqueScriptList = new List<TestEntry>();
        for (int i = 0; i < testEntryList.Count; ++i)
        {
            if (i == 0)
            {
                uniqueScriptList.Add(testEntryList[i]);
            }
            for (int j = 0; j < i; ++j)
            {
                if (testEntryList[j].scriptName == testEntryList[i].scriptName)
                {
                    break;
                }
                if (j == i - 1)
                {
                    uniqueScriptList.Add(testEntryList[i]);
                }
            }
        }

        return uniqueScriptList;
    }


}
